/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * language-support-plugin
 * 
 * language-support-plugin is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * language-support-plugin is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>
#include <gio/gio.h>

#include "ijs-symbol.h"
#include "node-symbol.h"
#include "local-symbol.h"
#include "util.h"

static void local_symbol_interface_init (IJsSymbolIface *iface);
static GList* local_symbol_get_arg_list (IJsSymbol *obj);
static gint local_symbol_get_base_type (IJsSymbol *obj);
static GList* local_symbol_get_func_ret_type (IJsSymbol *obj);
static IJsSymbol* local_symbol_get_member (IJsSymbol *obj, const gchar * name);
static const gchar * local_symbol_get_name (IJsSymbol *obj);
static gboolean local_symbol_is_deleted (IJsSymbol *obj);
static GList* local_symbol_list_member (IJsSymbol *obj);

static GList* get_var_list (gint lineno, Context *my_cx);

G_DEFINE_TYPE_WITH_CODE (LocalSymbol, local_symbol, G_TYPE_OBJECT,
			G_IMPLEMENT_INTERFACE (IJS_TYPE_SYMBOL,
						local_symbol_interface_init));

typedef struct _LocalSymbolPrivate LocalSymbolPrivate;
struct _LocalSymbolPrivate
{
	Context *my_cx;
	JSParseNode *node;
	GList *missed_semicolon;
	gchar *self_name;
	GList *calls;
};

#define LOCAL_SYMBOL_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), LOCAL_TYPE_SYMBOL, LocalSymbolPrivate))

static void
local_symbol_init (LocalSymbol *object)
{
	LocalSymbolPrivate *priv = LOCAL_SYMBOL_PRIVATE (object);
	priv->my_cx = NULL;
	priv->node = NULL;
	priv->self_name = NULL;
	priv->calls = NULL;
	priv->missed_semicolon = NULL;
}

static void
local_symbol_finalize (GObject *object)
{
	LocalSymbolPrivate *priv = LOCAL_SYMBOL_PRIVATE (object);

	g_list_free (priv->calls);
	g_list_free (priv->missed_semicolon);
	g_free (priv->self_name);
	js_context_free (priv->my_cx);
	jsnode_free (priv->node);

	G_OBJECT_CLASS (local_symbol_parent_class)->finalize (object);
}

static void
local_symbol_class_init (LocalSymbolClass *klass)
{
	GObjectClass* object_class = G_OBJECT_CLASS (klass);
	GObjectClass* parent_class = G_OBJECT_CLASS (klass);

	g_type_class_add_private (klass, sizeof (LocalSymbolPrivate));

	object_class->finalize = local_symbol_finalize;
}


LocalSymbol*
local_symbol_new (const gchar *filename)
{
	LocalSymbol* ret = LOCAL_SYMBOL (g_object_new (LOCAL_TYPE_SYMBOL, NULL));
	LocalSymbolPrivate *priv = LOCAL_SYMBOL_PRIVATE (ret);

	priv->node = parse_file (filename);
	priv->missed_semicolon = g_list_copy (get_lines_missed_semicolon ());
	priv->my_cx = js_context_new (NULL);
	priv->calls = NULL;
	interpretator(priv->node, priv->my_cx, &priv->calls);

	GFile *file = g_file_new_for_path (filename);
	priv->self_name = g_file_get_basename (file);
	if (strcmp (priv->self_name + strlen (priv->self_name) - 3, ".js") == 0)
		priv->self_name[strlen (priv->self_name) - 3] = '\0';
	return ret;
}

GList*
local_symbol_get_missed_semicolons (LocalSymbol* object)
{
	g_assert (LOCAL_IS_SYMBOL (object));
	LocalSymbolPrivate *priv = LOCAL_SYMBOL_PRIVATE (object);
	return priv->missed_semicolon;
}

GList*
local_symbol_list_member_with_line (LocalSymbol* object, gint line)
{
	g_assert (LOCAL_IS_SYMBOL (object));
	LocalSymbolPrivate *priv = LOCAL_SYMBOL_PRIVATE (object);

	return get_var_list(line, priv->my_cx);
}

static void
local_symbol_interface_init (IJsSymbolIface *iface)
{
	iface->get_arg_list = local_symbol_get_arg_list;
	iface->get_base_type = local_symbol_get_base_type;
	iface->get_func_ret_type = local_symbol_get_func_ret_type;

	iface->get_member = local_symbol_get_member;
	iface->get_name = local_symbol_get_name;
	iface->is_deleted = local_symbol_is_deleted;
	iface->list_member = local_symbol_list_member;
}

static GList*
local_symbol_get_arg_list (IJsSymbol *obj)
{
	g_assert_not_reached ();
}

static gint
local_symbol_get_base_type (IJsSymbol *obj)
{
	return BASE_CLASS;
}

static GList*
local_symbol_get_func_ret_type (IJsSymbol *obj)
{
	g_assert_not_reached ();
}

static IJsSymbol*
local_symbol_get_member (IJsSymbol *obj, const gchar * name)
{
	LocalSymbol* self = LOCAL_SYMBOL (obj);
	LocalSymbolPrivate *priv = LOCAL_SYMBOL_PRIVATE (self);

	JSParseNode *node = find_last_assignment (name, priv->my_cx);
	if (node)
		return IJS_SYMBOL (node_symbol_new (node, name, priv->my_cx));

	return NULL;
}

static const gchar *
local_symbol_get_name (IJsSymbol *obj)
{
	LocalSymbol* self = LOCAL_SYMBOL (obj);
	LocalSymbolPrivate *priv = LOCAL_SYMBOL_PRIVATE (self);

	return g_strdup (priv->self_name);
}

static gboolean
local_symbol_is_deleted (IJsSymbol *obj)
{
	return TRUE;
}

static GList*
local_symbol_list_member (IJsSymbol *obj)
{
	LocalSymbol* self = LOCAL_SYMBOL (obj);
	LocalSymbolPrivate *priv = LOCAL_SYMBOL_PRIVATE (self);

	return get_var_list(0, priv->my_cx);
}

static GList*
get_var_list (gint lineno, Context *my_cx)
{
//	print (my_cx, "|");
	GList *i, *ret = NULL;
	for (i = my_cx->local_var; i; i = g_list_next (i))
	{
		Var *t = (Var *)i->data;
		if (!t->name)
			continue;
		ret = g_list_append (ret, t->name);
	}
	for (i = g_list_last (my_cx->childs); i; i = g_list_previous (i))
	{
		Context *t = (Context *)i->data;
//		printf ("line %d, (%d/%d)\n", lineno, t->bline, t->eline);
		if (lineno)
			if (t->bline > lineno || t->eline + 2 < lineno)
				continue;
		ret = g_list_concat (ret, get_var_list (lineno, t));
	}
	if (my_cx->func_name/* && lineno*/)
	{
		ret = g_list_concat (ret, my_cx->func_arg);
	}
	return ret;
}
